﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace VIA.SiMoJi.Domain.Vision
{
    public struct CMatrix
    {
        private static CMatrix s_identity = CreateIdentity();

        private const int c_identityHashCode = 0;

        internal double _m11 = 1.0;

        internal double _m12 = 0.0;

        internal double _m21 = 0.0;

        internal double _m22 = 1.0;

        internal double _offsetX = 0.0;

        internal double _offsetY = 0.0;

        internal MatrixTypes _type;

        internal int _padding = 0;

        /// <summary>
        /// 获取一个单位矩阵
        /// </summary>
        public static CMatrix Identity => s_identity;

        /// <summary>
        /// 是否有是单位矩阵
        /// </summary>
        public bool IsIdentity
        {
            get
            {
                if (_type != 0)
                {
                    if (_m11 == 1.0 && _m12 == 0.0 && _m21 == 0.0 && _m22 == 1.0 && _offsetX == 0.0)
                    {
                        return _offsetY == 0.0;
                    }

                    return false;
                }
                return true;
            }
        }

        /// <summary>
        /// 行列式
        /// </summary>
        public double Determinant
        {
            get
            {
                switch (_type)
                {
                    case MatrixTypes.TRANSFORM_IS_IDENTITY:
                    case MatrixTypes.TRANSFORM_IS_TRANSLATION:
                        return 1.0;
                    case MatrixTypes.TRANSFORM_IS_SCALING:
                    case MatrixTypes.TRANSFORM_IS_TRANSLATION | MatrixTypes.TRANSFORM_IS_SCALING:
                        return _m11 * _m22;
                    default:
                        return _m11 * _m22 - _m12 * _m21;
                }
            }
        }

        /// <summary>
        /// 是否有可逆
        /// </summary>
        public bool HasInverse => !(Math.Abs(Determinant) < 2.2204460492503131E-15);

        public double M11
        {
            get
            {
                if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY)
                {
                    return 1.0;
                }

                return _m11;
            }
            set
            {
                if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY)
                {
                    SetMatrix(value, 0.0, 0.0, 1.0, 0.0, 0.0, MatrixTypes.TRANSFORM_IS_SCALING);
                    return;
                }

                _m11 = value;
                if (_type != MatrixTypes.TRANSFORM_IS_UNKNOWN)
                {
                    _type |= MatrixTypes.TRANSFORM_IS_SCALING;
                }
            }
        }

        public double M12
        {
            get
            {
                if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY)
                {
                    return 0.0;
                }

                return _m12;
            }
            set
            {
                if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY)
                {
                    SetMatrix(1.0, value, 0.0, 1.0, 0.0, 0.0, MatrixTypes.TRANSFORM_IS_UNKNOWN);
                    return;
                }

                _m12 = value;
                _type = MatrixTypes.TRANSFORM_IS_UNKNOWN;
            }
        }

        public double M21
        {
            get
            {
                if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY)
                {
                    return 0.0;
                }

                return _m21;
            }
            set
            {
                if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY)
                {
                    SetMatrix(1.0, 0.0, value, 1.0, 0.0, 0.0, MatrixTypes.TRANSFORM_IS_UNKNOWN);
                    return;
                }

                _m21 = value;
                _type = MatrixTypes.TRANSFORM_IS_UNKNOWN;
            }
        }

        public double M22
        {
            get
            {
                if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY)
                {
                    return 1.0;
                }

                return _m22;
            }
            set
            {
                if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY)
                {
                    SetMatrix(1.0, 0.0, 0.0, value, 0.0, 0.0, MatrixTypes.TRANSFORM_IS_SCALING);
                    return;
                }

                _m22 = value;
                if (_type != MatrixTypes.TRANSFORM_IS_UNKNOWN)
                {
                    _type |= MatrixTypes.TRANSFORM_IS_SCALING;
                }
            }
        }

        public double OffsetX
        {
            get
            {
                if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY)
                {
                    return 0.0;
                }

                return _offsetX;
            }
            set
            {
                if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY)
                {
                    SetMatrix(1.0, 0.0, 0.0, 1.0, value, 0.0, MatrixTypes.TRANSFORM_IS_TRANSLATION);
                    return;
                }

                _offsetX = value;
                if (_type != MatrixTypes.TRANSFORM_IS_UNKNOWN)
                {
                    _type |= MatrixTypes.TRANSFORM_IS_TRANSLATION;
                }
            }
        }

        public double OffsetY
        {
            get
            {
                if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY)
                {
                    return 0.0;
                }

                return _offsetY;
            }
            set
            {
                if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY)
                {
                    SetMatrix(1.0, 0.0, 0.0, 1.0, 0.0, value, MatrixTypes.TRANSFORM_IS_TRANSLATION);
                    return;
                }

                _offsetY = value;
                if (_type != MatrixTypes.TRANSFORM_IS_UNKNOWN)
                {
                    _type |= MatrixTypes.TRANSFORM_IS_TRANSLATION;
                }
            }
        }

        private bool IsDistinguishedIdentity => _type == MatrixTypes.TRANSFORM_IS_IDENTITY;

        public CMatrix() { _m11 = 1.0; _m12 = 0; _m21 = 0; _m22 = 1.0; _type = MatrixTypes.TRANSFORM_IS_IDENTITY; _padding = 0; }

        public CMatrix(double m11, double m12, double m21, double m22, double offsetX, double offsetY)
        {
            _m11 = m11;
            _m12 = m12;
            _m21 = m21;
            _m22 = m22;
            _offsetX = offsetX;
            _offsetY = offsetY;
            _type = MatrixTypes.TRANSFORM_IS_UNKNOWN;
            _padding = 0;
            DeriveMatrixType();
        }

        public void SetIdentity()
        {
            SetMatrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, MatrixTypes.TRANSFORM_IS_IDENTITY);
        }

        public static CMatrix operator *(CMatrix trans1, CMatrix trans2)
        {
            MatrixUtil.MultiplyMatrix(ref trans1, ref trans2);
            return trans1;
        }

        public static CMatrix Multiply(CMatrix trans1, CMatrix trans2)
        {
            MatrixUtil.MultiplyMatrix(ref trans1, ref trans2);
            return trans1;
        }

        public void Append(CMatrix matrix)
        {
            this *= matrix;
        }

        public void Prepend(CMatrix matrix)
        {
            this = matrix * this;
        }

        public void Rotate(double angle)
        {
            angle %= 360.0;
            this *= CreateRotationRadians(angle * (Math.PI / 180.0));
        }

        public void RotatePrepend(double angle)
        {
            angle %= 360.0;
            this = CreateRotationRadians(angle * (Math.PI / 180.0)) * this;
        }

        public void RotateAt(double angle, double centerX, double centerY)
        {
            angle %= 360.0;
            this *= CreateRotationRadians(angle * (Math.PI / 180.0), centerX, centerY);
        }

        public void RotateAtPrepend(double angle, double centerX, double centerY)
        {
            angle %= 360.0;
            this = CreateRotationRadians(angle * (Math.PI / 180.0), centerX, centerY) * this;
        }

        public void Scale(double scaleX, double scaleY)
        {
            this *= CreateScaling(scaleX, scaleY);
        }

        public void ScalePrepend(double scaleX, double scaleY)
        {
            this = CreateScaling(scaleX, scaleY) * this;
        }

        public void ScaleAt(double scaleX, double scaleY, double centerX, double centerY)
        {
            this *= CreateScaling(scaleX, scaleY, centerX, centerY);
        }

        public void ScaleAtPrepend(double scaleX, double scaleY, double centerX, double centerY)
        {
            this = CreateScaling(scaleX, scaleY, centerX, centerY) * this;
        }

        public void Skew(double skewX, double skewY)
        {
            skewX %= 360.0;
            skewY %= 360.0;
            this *= CreateSkewRadians(skewX * (Math.PI / 180.0), skewY * (Math.PI / 180.0));
        }

        public void SkewPrepend(double skewX, double skewY)
        {
            skewX %= 360.0;
            skewY %= 360.0;
            this = CreateSkewRadians(skewX * (Math.PI / 180.0), skewY * (Math.PI / 180.0)) * this;
        }

        public void Translate(double offsetX, double offsetY)
        {
            if (_type == MatrixTypes.TRANSFORM_IS_IDENTITY)
            {
                SetMatrix(1.0, 0.0, 0.0, 1.0, offsetX, offsetY, MatrixTypes.TRANSFORM_IS_TRANSLATION);
            }
            else if (_type == MatrixTypes.TRANSFORM_IS_UNKNOWN)
            {
                _offsetX += offsetX;
                _offsetY += offsetY;
            }
            else
            {
                _offsetX += offsetX;
                _offsetY += offsetY;
                _type |= MatrixTypes.TRANSFORM_IS_TRANSLATION;
            }
        }

        public void TranslatePrepend(double offsetX, double offsetY)
        {
            this = CreateTranslation(offsetX, offsetY) * this;
        }

        /// <summary>
        /// 变换成逆矩阵
        /// </summary>
        /// <exception cref="InvalidOperationException"></exception>
        public void Invert()
        {
            double determinant = Determinant;
            if ((Math.Abs(determinant) < 2.2204460492503131E-15))
            {
                throw new InvalidOperationException("Transform_NotInvertible");
            }

            switch (_type)
            {
                case MatrixTypes.TRANSFORM_IS_SCALING:
                    _m11 = 1.0 / _m11;
                    _m22 = 1.0 / _m22;
                    break;
                case MatrixTypes.TRANSFORM_IS_TRANSLATION:
                    _offsetX = 0.0 - _offsetX;
                    _offsetY = 0.0 - _offsetY;
                    break;
                case MatrixTypes.TRANSFORM_IS_TRANSLATION | MatrixTypes.TRANSFORM_IS_SCALING:
                    _m11 = 1.0 / _m11;
                    _m22 = 1.0 / _m22;
                    _offsetX = (0.0 - _offsetX) * _m11;
                    _offsetY = (0.0 - _offsetY) * _m22;
                    break;
                default:
                    {
                        double num = 1.0 / determinant;
                        SetMatrix(_m22 * num, (0.0 - _m12) * num, (0.0 - _m21) * num, _m11 * num, (_m21 * _offsetY - _offsetX * _m22) * num, (_offsetX * _m12 - _m11 * _offsetY) * num, MatrixTypes.TRANSFORM_IS_UNKNOWN);
                        break;
                    }
                case MatrixTypes.TRANSFORM_IS_IDENTITY:
                    break;
            }
        }

        public Point Transform(Point point)
        {
            double x = point.X;
            double y = point.Y;
            MultiplyPoint(ref x, ref y);
            Point result = new Point((int)x, (int)y);
            return result;
        }

        public PointF Transform(PointF point)
        {
            double x = point.X;
            double y = point.Y;
            MultiplyPoint(ref x, ref y);
            PointF result = new PointF((float)x, (float)y);
            return result;
        }

        public void Transform(Point[] points)
        {
            Point[] temp = new Point[points.Length];
            if (points != null)
            {
                for (int i = 0; i < points.Length; i++)
                {
                    temp[i] = Transform(points[i]);
                }
            }
        }

        public void Transform(PointF[] points)
        {
            PointF[] temp = new PointF[points.Length];
            if (points != null)
            {
                for (int i = 0; i < points.Length; i++)
                {
                    temp[i] = Transform(points[i]);
                }
            }
        }

        public void MultiplyVector(ref double x, ref double y)
        {
            switch (_type)
            {
                case MatrixTypes.TRANSFORM_IS_IDENTITY:
                    return;
                case MatrixTypes.TRANSFORM_IS_TRANSLATION:
                case MatrixTypes.TRANSFORM_IS_SCALING:
                case MatrixTypes.TRANSFORM_IS_TRANSLATION | MatrixTypes.TRANSFORM_IS_SCALING:
                    x *= _m11;
                    y *= _m22;
                    return;
            }

            double num = y * _m21;
            double num2 = x * _m12;
            x *= _m11;
            x += num;
            y *= _m22;
            y += num2;
            // x = x * _m11 + y * _m21 ;
            // y = x * _m12 + y * _m22;
        }

        internal void MultiplyPoint(ref double x, ref double y)
        {
            switch (_type)
            {
                case MatrixTypes.TRANSFORM_IS_IDENTITY:
                    break;
                case MatrixTypes.TRANSFORM_IS_TRANSLATION:
                    x += _offsetX;
                    y += _offsetY;
                    break;
                case MatrixTypes.TRANSFORM_IS_SCALING:
                    x *= _m11;
                    y *= _m22;
                    break;
                case MatrixTypes.TRANSFORM_IS_TRANSLATION | MatrixTypes.TRANSFORM_IS_SCALING:
                    x *= _m11;
                    x += _offsetX;
                    y *= _m22;
                    y += _offsetY;
                    break;
                default:
                    {
                        double num = y * _m21 + _offsetX;
                        double num2 = x * _m12 + _offsetY;
                        x *= _m11;
                        x += num;
                        y *= _m22;
                        y += num2;
                        // x = x * _m11 + y * _m21 + _offsetX;
                        // y = x * _m12 + y * _m22 + _offsetY;
                        break;
                    }
            }
        }

        internal static CMatrix CreateRotationRadians(double angle)
        {
            return CreateRotationRadians(angle, 0.0, 0.0);
        }

        internal static CMatrix CreateRotationRadians(double angle, double centerX, double centerY)
        {
            CMatrix result = default;
            double sin = Math.Sin(angle);
            double cos = Math.Cos(angle);
            double offsetX = centerX * (1.0 - cos) + centerY * sin;
            double offsetY = centerY * (1.0 - cos) - centerX * sin;
            result.SetMatrix(cos, sin, 0.0 - sin, cos, offsetX, offsetY, MatrixTypes.TRANSFORM_IS_UNKNOWN);//原版
            return result;
        }

        internal static CMatrix CreateScaling(double scaleX, double scaleY, double centerX, double centerY)
        {
            CMatrix result = default(CMatrix);
            result.SetMatrix(scaleX, 0.0, 0.0, scaleY, centerX - scaleX * centerX, centerY - scaleY * centerY, MatrixTypes.TRANSFORM_IS_TRANSLATION | MatrixTypes.TRANSFORM_IS_SCALING);
            return result;
        }

        internal static CMatrix CreateScaling(double scaleX, double scaleY)
        {
            CMatrix result = default(CMatrix);
            result.SetMatrix(scaleX, 0.0, 0.0, scaleY, 0.0, 0.0, MatrixTypes.TRANSFORM_IS_SCALING);
            return result;
        }

        internal static CMatrix CreateSkewRadians(double skewX, double skewY)
        {
            CMatrix result = default(CMatrix);
            result.SetMatrix(1.0, Math.Tan(skewY), Math.Tan(skewX), 1.0, 0.0, 0.0, MatrixTypes.TRANSFORM_IS_UNKNOWN);
            return result;
        }

        internal static CMatrix CreateTranslation(double offsetX, double offsetY)
        {
            CMatrix result = default(CMatrix);
            result.SetMatrix(1.0, 0.0, 0.0, 1.0, offsetX, offsetY, MatrixTypes.TRANSFORM_IS_TRANSLATION);
            return result;
        }

        private static CMatrix CreateIdentity()
        {
            CMatrix result = default(CMatrix);
            result.SetMatrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, MatrixTypes.TRANSFORM_IS_IDENTITY);
            return result;
        }

        private void SetMatrix(double m11, double m12, double m21, double m22, double offsetX, double offsetY, MatrixTypes type)
        {
            _m11 = m11;
            _m12 = m12;
            _m21 = m21;
            _m22 = m22;
            _offsetX = offsetX;
            _offsetY = offsetY;
            _type = type;
        }

        private void DeriveMatrixType()
        {
            _type = MatrixTypes.TRANSFORM_IS_IDENTITY;
            if (_m21 != 0.0 || _m12 != 0.0)
            {
                _type = MatrixTypes.TRANSFORM_IS_UNKNOWN;
                return;
            }

            if (_m11 != 1.0 || _m22 != 1.0)
            {
                _type = MatrixTypes.TRANSFORM_IS_SCALING;
            }

            if (_offsetX != 0.0 || _offsetY != 0.0)
            {
                _type |= MatrixTypes.TRANSFORM_IS_TRANSLATION;
            }

            if ((_type & (MatrixTypes.TRANSFORM_IS_TRANSLATION | MatrixTypes.TRANSFORM_IS_SCALING)) == 0)
            {
                _type = MatrixTypes.TRANSFORM_IS_IDENTITY;
            }
        }

        [Conditional("DEBUG")]
        private void Debug_CheckType()
        {
            switch (_type)
            {
                case MatrixTypes.TRANSFORM_IS_IDENTITY:
                    break;
                case MatrixTypes.TRANSFORM_IS_UNKNOWN:
                    break;
                case MatrixTypes.TRANSFORM_IS_SCALING:
                    break;
                case MatrixTypes.TRANSFORM_IS_TRANSLATION:
                    break;
                case MatrixTypes.TRANSFORM_IS_TRANSLATION | MatrixTypes.TRANSFORM_IS_SCALING:
                    break;
            }
        }

        public static bool operator ==(CMatrix matrix1, CMatrix matrix2)
        {
            if (matrix1.IsDistinguishedIdentity || matrix2.IsDistinguishedIdentity)
            {
                return matrix1.IsIdentity == matrix2.IsIdentity;
            }

            if (matrix1.M11 == matrix2.M11 && matrix1.M12 == matrix2.M12 && matrix1.M21 == matrix2.M21 && matrix1.M22 == matrix2.M22 && matrix1.OffsetX == matrix2.OffsetX)
            {
                return matrix1.OffsetY == matrix2.OffsetY;
            }

            return false;
        }

        public static bool operator !=(CMatrix matrix1, CMatrix matrix2)
        {
            return !(matrix1 == matrix2);
        }

        public static bool Equals(CMatrix matrix1, CMatrix matrix2)
        {
            if (matrix1.IsDistinguishedIdentity || matrix2.IsDistinguishedIdentity)
            {
                return matrix1.IsIdentity == matrix2.IsIdentity;
            }

            if (matrix1.M11.Equals(matrix2.M11) && matrix1.M12.Equals(matrix2.M12) && matrix1.M21.Equals(matrix2.M21) && matrix1.M22.Equals(matrix2.M22) && matrix1.OffsetX.Equals(matrix2.OffsetX))
            {
                return matrix1.OffsetY.Equals(matrix2.OffsetY);
            }

            return false;
        }

        public override bool Equals(object o)
        {
            if (o == null || !(o is CMatrix))
            {
                return false;
            }

            CMatrix matrix = (CMatrix)o;
            return Equals(this, matrix);
        }

        public bool Equals(CMatrix value)
        {
            return Equals(this, value);
        }

        public override int GetHashCode()
        {
            if (IsDistinguishedIdentity)
            {
                return 0;
            }

            return M11.GetHashCode() ^ M12.GetHashCode() ^ M21.GetHashCode() ^ M22.GetHashCode() ^ OffsetX.GetHashCode() ^ OffsetY.GetHashCode();
        }

        public override string ToString()
        {
            return $"{_m11:0.##########},{_m12:0.##########},{_m21:0.##########},{_m22:0.#########},{_offsetX:0.#########},{_offsetY:0.#########}";
        }

        /*
        public string ToString(IFormatProvider provider)
        {
            return ConvertToString(null, provider);
        }

        string IFormattable.ToString(string format, IFormatProvider provider)
        {
            return ConvertToString(format, provider);
        }

        internal string ConvertToString(string format, IFormatProvider provider)
        {
            if (IsIdentity)
            {
                return "Identity";
            }

            char numericListSeparator = TokenizerHelper.GetNumericListSeparator(provider);
            return string.Format(provider, "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + "}{0}{6:" + format + "}", numericListSeparator, _m11, _m12, _m21, _m22, _offsetX, _offsetY);
        }
        */
    }

    internal enum MatrixTypes
    {
        TRANSFORM_IS_IDENTITY = 0x0,
        TRANSFORM_IS_TRANSLATION = 0x1,
        TRANSFORM_IS_SCALING = 0x2,
        TRANSFORM_IS_UNKNOWN = 0x4
    }

    internal static class MatrixUtil
    {
        //internal static void TransformRect(ref Rectangle rect, ref ColMatrix matrix)
        //{
        //    if (rect.IsEmpty)
        //    {
        //        return;
        //    }

        //    MatrixTypes type = matrix._type;
        //    if (type == MatrixTypes.TRANSFORM_IS_IDENTITY)
        //    {
        //        return;
        //    }

        //    if ((type & MatrixTypes.TRANSFORM_IS_SCALING) != 0)
        //    {
        //        rect.X *= matrix._m11;
        //        rect.Y *= matrix._m22;
        //        rect.Width *= matrix._m11;
        //        rect.Height *= matrix._m22;
        //        if (rect._width < 0.0)
        //        {
        //            rect._x += rect._width;
        //            rect._width = 0.0 - rect._width;
        //        }

        //        if (rect._height < 0.0)
        //        {
        //            rect._y += rect._height;
        //            rect._height = 0.0 - rect._height;
        //        }
        //    }

        //    if ((type & MatrixTypes.TRANSFORM_IS_TRANSLATION) != 0)
        //    {
        //        rect._x += matrix._offsetX;
        //        rect._y += matrix._offsetY;
        //    }

        //    if (type == MatrixTypes.TRANSFORM_IS_UNKNOWN)
        //    {
        //        Point point = matrix.Transform(rect.TopLeft);
        //        Point point2 = matrix.Transform(rect.TopRight);
        //        Point point3 = matrix.Transform(rect.BottomRight);
        //        Point point4 = matrix.Transform(rect.BottomLeft);
        //        rect._x = Math.Min(Math.Min(point.X, point2.X), Math.Min(point3.X, point4.X));
        //        rect._y = Math.Min(Math.Min(point.Y, point2.Y), Math.Min(point3.Y, point4.Y));
        //        rect._width = Math.Max(Math.Max(point.X, point2.X), Math.Max(point3.X, point4.X)) - rect._x;
        //        rect._height = Math.Max(Math.Max(point.Y, point2.Y), Math.Max(point3.Y, point4.Y)) - rect._y;
        //    }
        //}

        //public Vector Transform(Vector vector)
        //{
        //    Vector result = vector;
        //    MultiplyVector(ref result._x, ref result._y);
        //    return result;
        //}

        //public void Transform(Vector[] vectors)
        //{
        //    if (vectors != null)
        //    {
        //        for (int i = 0; i < vectors.Length; i++)
        //        {
        //            MultiplyVector(ref vectors[i]._x, ref vectors[i]._y);
        //        }
        //    }
        //}

        internal static void MultiplyMatrix(ref CMatrix matrix1, ref CMatrix matrix2)
        {
            MatrixTypes type = matrix1._type;
            MatrixTypes type2 = matrix2._type;
            if (type2 == MatrixTypes.TRANSFORM_IS_IDENTITY)
            {
                return;
            }

            if (type == MatrixTypes.TRANSFORM_IS_IDENTITY)
            {
                matrix1 = matrix2;
                return;
            }

            if (type2 == MatrixTypes.TRANSFORM_IS_TRANSLATION)
            {
                matrix1._offsetX += matrix2._offsetX;
                matrix1._offsetY += matrix2._offsetY;
                if (type != MatrixTypes.TRANSFORM_IS_UNKNOWN)
                {
                    matrix1._type |= MatrixTypes.TRANSFORM_IS_TRANSLATION;
                }

                return;
            }

            if (type == MatrixTypes.TRANSFORM_IS_TRANSLATION)
            {
                double offsetX = matrix1._offsetX;
                double offsetY = matrix1._offsetY;
                matrix1 = matrix2;
                matrix1._offsetX = offsetX * matrix2._m11 + offsetY * matrix2._m21 + matrix2._offsetX;
                matrix1._offsetY = offsetX * matrix2._m12 + offsetY * matrix2._m22 + matrix2._offsetY;
                if (type2 == MatrixTypes.TRANSFORM_IS_UNKNOWN)
                {
                    matrix1._type = MatrixTypes.TRANSFORM_IS_UNKNOWN;
                }
                else
                {
                    matrix1._type = MatrixTypes.TRANSFORM_IS_TRANSLATION | MatrixTypes.TRANSFORM_IS_SCALING;
                }

                return;
            }

            switch ((uint)((int)type << 4) | (uint)type2)
            {
                case 34u:
                    matrix1._m11 *= matrix2._m11;
                    matrix1._m22 *= matrix2._m22;
                    break;
                case 35u:
                    matrix1._m11 *= matrix2._m11;
                    matrix1._m22 *= matrix2._m22;
                    matrix1._offsetX = matrix2._offsetX;
                    matrix1._offsetY = matrix2._offsetY;
                    matrix1._type = MatrixTypes.TRANSFORM_IS_TRANSLATION | MatrixTypes.TRANSFORM_IS_SCALING;
                    break;
                case 50u:
                    matrix1._m11 *= matrix2._m11;
                    matrix1._m22 *= matrix2._m22;
                    matrix1._offsetX *= matrix2._m11;
                    matrix1._offsetY *= matrix2._m22;
                    break;
                case 51u:
                    matrix1._m11 *= matrix2._m11;
                    matrix1._m22 *= matrix2._m22;
                    matrix1._offsetX = matrix2._m11 * matrix1._offsetX + matrix2._offsetX;
                    matrix1._offsetY = matrix2._m22 * matrix1._offsetY + matrix2._offsetY;
                    break;
                case 36u:
                case 52u:
                case 66u:
                case 67u:
                case 68u:
                    matrix1 = new CMatrix(matrix1._m11 * matrix2._m11 + matrix1._m12 * matrix2._m21, matrix1._m11 * matrix2._m12 + matrix1._m12 * matrix2._m22, matrix1._m21 * matrix2._m11 + matrix1._m22 * matrix2._m21, matrix1._m21 * matrix2._m12 + matrix1._m22 * matrix2._m22, matrix1._offsetX * matrix2._m11 + matrix1._offsetY * matrix2._m21 + matrix2._offsetX, matrix1._offsetX * matrix2._m12 + matrix1._offsetY * matrix2._m22 + matrix2._offsetY);
                    break;
            }
        }

        internal static void PrependOffset(ref CMatrix matrix, double offsetX, double offsetY)
        {
            if (matrix._type == MatrixTypes.TRANSFORM_IS_IDENTITY)
            {
                matrix = new CMatrix(1.0, 0.0, 0.0, 1.0, offsetX, offsetY);
                matrix._type = MatrixTypes.TRANSFORM_IS_TRANSLATION;
                return;
            }

            matrix._offsetX += matrix._m11 * offsetX + matrix._m21 * offsetY;
            matrix._offsetY += matrix._m12 * offsetX + matrix._m22 * offsetY;
            if (matrix._type != MatrixTypes.TRANSFORM_IS_UNKNOWN)
            {
                matrix._type |= MatrixTypes.TRANSFORM_IS_TRANSLATION;
            }
        }
    }
}
